package com.imrookie.wrap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Class包装类
 * @author wangjiyu@imdada.cn
 * @create 2018/12/13
 */
public class ClassWrap {

    private static final Logger logger = LoggerFactory.getLogger(ClassWrap.class);

    private String className;//类名
    private String wholeClassName;//类全名
    private Class clazz;//class引用
    private boolean classFlag;
    private boolean interfaceFlag;
    private boolean enumFlag;
    private boolean normalClass;//普通类型: 基本类型 + 基本类型的包装类型 + String + Date + Array + Collection + Map + Void + Object
    private FieldWrap[] fieldWraps;//字段数组
    private MethodWrap[] methodWraps;//方法数组

    private static final Map<Class, ClassWrap> CLASS_TOOL = new HashMap<Class, ClassWrap>();//加载池

    private ClassWrap() {

    }

    /**
     * 获取类包装对象
     * 保证每个类的包装对象都是单例且线程安全的(双重校验锁)
     * @param clz
     * @return
     */
    public static ClassWrap getInstance(Class clz) {
        while (clz.isArray()) {//数组类型转成元素类型, while可以解决多维数组问题
            clz = clz.getComponentType();
        }
        ClassWrap classWrap = CLASS_TOOL.get(clz);
        if (classWrap == null) {
            synchronized (ClassWrap.class) {
                if (CLASS_TOOL.get(clz) == null) {
                    classWrap = newInstance(clz);
                }
            }
        }
        return classWrap;
    }

    /**
     * 生产实例
     * @param clazz
     */
    private static ClassWrap newInstance(Class clazz){
        if (CLASS_TOOL.containsKey(clazz) && CLASS_TOOL.get(clazz) != null) {
            throw new RuntimeException("class is repetitive:" + clazz.getName());
        }
        ClassWrap classWrap = new ClassWrap();
        CLASS_TOOL.put(clazz, classWrap);//先占位,避免循环引用(类的属性之一是类自己)导致的死循环

        classWrap.className = clazz.getSimpleName();
        classWrap.wholeClassName = clazz.getName();
        classWrap.clazz = clazz;
        classWrap.interfaceFlag = clazz.isInterface();
        classWrap.classFlag = !classWrap.interfaceFlag;
        classWrap.enumFlag = clazz.isEnum();
        if (clazz.isPrimitive() || Byte.class.isAssignableFrom(clazz) || Short.class.isAssignableFrom(clazz)
                || Integer.class.isAssignableFrom(clazz) || Long.class.isAssignableFrom(clazz) || Float.class.isAssignableFrom(clazz)
                || Double.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz)
                || String.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz) || clazz.isArray()
                || Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || Void.class.isAssignableFrom(clazz) || Object.class.equals(clazz) ) {
            classWrap.normalClass = true;
        }
        //非常规类时,继续查找字段信息和方法
        if (!classWrap.normalClass) {
            if (classWrap.isEnumFlag()) {//枚举特殊处理
                Field[] fields = clazz.getDeclaredFields();
                if (fields != null && fields.length > 0) {
                    List<FieldWrap> list = new ArrayList<FieldWrap>(fields.length);
                    for (int i=0;i<fields.length;i++) {
                        Field field = fields[i];
                        if (field.getName().equals("$VALUES")) {
                            //logger.info("枚举类干掉$VALUES");
                            continue;
                        }
                        list.add(new FieldWrap(field));
                    }
                    classWrap.fieldWraps = list.toArray(new FieldWrap[]{});
                }
            } else {
                //字段
                Field[] fields = clazz.getDeclaredFields();
                if (fields != null && fields.length > 0) {
                    classWrap.fieldWraps = new FieldWrap[fields.length];
                    for (int i=0;i<fields.length;i++) {
                        Field field = fields[i];
                        classWrap.fieldWraps[i] = new FieldWrap(field);
                    }
                }
                //方法
                Method[] methods = clazz.getDeclaredMethods();
                if (methods != null && methods.length > 0) {
                    classWrap.methodWraps = new MethodWrap[methods.length];
                    for (int i=0;i<methods.length;i++) {
                        Method method = methods[i];
                        classWrap.methodWraps[i] = new MethodWrap(method);
                    }
                }
            }
        }
        logger.info("ClassWrap装配完毕: {}", clazz.getName());
        return classWrap;
    }

    /*private ClassWrap(Class clazz) {
        className = clazz.getSimpleName();
        wholeClassName = clazz.getName();
        this.clazz = clazz;
        interfaceFlag = clazz.isInterface();
        classFlag = !interfaceFlag;
        enumFlag = clazz.isEnum();
        if (clazz.isPrimitive() || Byte.class.isAssignableFrom(clazz) || Short.class.isAssignableFrom(clazz)
                || Integer.class.isAssignableFrom(clazz) || Long.class.isAssignableFrom(clazz) || Float.class.isAssignableFrom(clazz)
                || Double.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz)
                || String.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz) || clazz.isArray()
                || Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || Void.class.isAssignableFrom(clazz) ) {
            normalClass = true;
        }
        //非常规类时,继续查找字段信息和方法
        if (!normalClass) {
            //字段
            Field[] fields = clazz.getDeclaredFields();
            if (fields != null && fields.length > 0) {
                fieldWraps = new FieldWrap[fields.length];
                for (int i=0;i<fields.length;i++) {
                    Field field = fields[i];
                    fieldWraps[i] = new FieldWrap(field);
                }
            }
            //方法
            Method[] methods = clazz.getDeclaredMethods();
            if (methods != null && methods.length > 0) {
                System.out.println(methods.length+"个方法");
                methodWraps = new MethodWrap[methods.length];
                for (int i=0;i<methods.length;i++) {
                    Method method = methods[i];
                    methodWraps[i] = new MethodWrap(method);
                }
            }
        }
    }*/

    @Override
    public String toString() {
        return "ClassWrap{" +
                "className='" + className + '\'' +
                ", wholeClassName='" + wholeClassName + '\'' +
                ", classFlag=" + classFlag +
                ", interfaceFlag=" + interfaceFlag +
                ", enumFlag=" + enumFlag +
                ", normalClass=" + normalClass +
                ", fieldWraps=" + Arrays.toString(fieldWraps) +
                ", methodWraps=" + Arrays.toString(methodWraps) +
                '}';
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getWholeClassName() {
        return wholeClassName;
    }

    public void setWholeClassName(String wholeClassName) {
        this.wholeClassName = wholeClassName;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public boolean isNormalClass() {
        return normalClass;
    }

    public void setNormalClass(boolean normalClass) {
        this.normalClass = normalClass;
    }

    public FieldWrap[] getFieldWraps() {
        return fieldWraps;
    }

    public void setFieldWraps(FieldWrap[] fieldWraps) {
        this.fieldWraps = fieldWraps;
    }

    public boolean isClassFlag() {
        return classFlag;
    }

    public void setClassFlag(boolean classFlag) {
        this.classFlag = classFlag;
    }

    public boolean isInterfaceFlag() {
        return interfaceFlag;
    }

    public void setInterfaceFlag(boolean interfaceFlag) {
        this.interfaceFlag = interfaceFlag;
    }

    public boolean isEnumFlag() {
        return enumFlag;
    }

    public void setEnumFlag(boolean enumFlag) {
        this.enumFlag = enumFlag;
    }

    public MethodWrap[] getMethodWraps() {
        return methodWraps;
    }

    public void setMethodWraps(MethodWrap[] methodWraps) {
        this.methodWraps = methodWraps;
    }
}
